home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Speccy ClassiX 1998
/
Speccy ClassiX 98.iso
/
amiga_system
/
the_aminet
/
dev
/
gcc
/
ixemulsrc.lha
/
ixemul-41.4
/
library
/
tracecntl.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-05-28
|
9KB
|
281 lines
/*
* This file is part of ixemul.library for the Amiga.
* Copyright (C) 1991, 1992 Markus M. Wild
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* tracecntl.c,v 1.1.1.1 1994/04/04 04:29:49 amiga Exp
*
* tracecntl.c,v
* Revision 1.1.1.1 1994/04/04 04:29:49 amiga
* Initial CVS check in.
*
* Revision 1.6 1993/11/05 22:03:32 mwild
* treat vfork2 same as vfork
*
* Revision 1.5 1992/08/09 21:01:09 amiga
* import sysbase
*
* Revision 1.4 1992/07/04 19:23:16 mwild
* double the number of passed parameters. Probably still not enough for weird
* cases, but I can't do much about that...
*
* Revision 1.3 1992/05/22 01:51:07 mwild
* all common double returning functions are _JMP or they won't work
*
* Revision 1.2 1992/05/18 12:24:20 mwild
* new way of getting at the result of a function. Do the call from
* inside the handler and tell the library call hook to not call the
* function again (TRACE_ACTION_RTS). Removed trace_exit() handler.
*
* Revision 1.1 1992/05/14 19:55:40 mwild
* Initial revision
*
*/
#define KERNEL
#include "ixemul.h"
#include "kprintf.h"
#include <sys/tracecntl.h>
#ifdef TRACE_LIBRARY
struct ExecBase *SysBase;
static struct List packets = {
(struct Node *) &packets.lh_Tail, 0, (struct Node *) &packets.lh_Head,
};
static struct SignalSemaphore psem;
static sem_initialized = 0;
/* for each function traced, the trace_entry() function decides whether
the trace_exit() function is invoked or not.
If trace_entry() returns false, trace_exit() is not invoked.
NOTE: having setjmp, vfork and the like invoke trace_exit will *NOT*
work and will cause crashes!!
Since I consider placing break points just to get the return value
a bit overkill, I'll take a less optimal solution: I copy 8 args, this
will do for 99% of all cases, and some nasty printf() style call will
probably fail if tracing is enabled, so what ;-)) */
int
trace_entry (int scall, int (*func)(int, ...), void *ret_addr,
int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8,
int a9, int aa, int ab, int ac, int ad, int ae, int af)
{
struct trace_packet *tp, *ntp;
struct Task *me = FindTask (0);
int te_action, handler_active;
int omask;
/* for safety, don't do anything if running in Forbid() or Disable() */
if (SysBase->TDNestCnt >= 0 || SysBase->IDNestCnt >= 0)
return TRACE_ACTION_JMP;
/* have to do this here, or the handler may get into a deadlock
situation when trying to obtain the semaphore */
if (u.u_trace_flags)
return TRACE_ACTION_JMP;
/* get default action value */
switch (scall)
{
case SYS_abort:
case SYS_exit:
case SYS_longjmp:
case SYS_setjmp: /* can return twice ! */
case SYS_siglongjmp:
case SYS_sigreturn:
case SYS_sigsetjmp: /* "" */
case SYS__exit:
case SYS__longjmp:
case SYS__setjmp: /* "" */
case SYS_vfork: /* "" */
case SYS_vfork2: /* "" */
case SYS_vfork_resume: /* does longjmp-y thing... */
case SYS_execve:
case SYS_ix_geta4: /* special, result is in A4, not D0 ;-)) */
case SYS_ix_check_cpu: /* may not return (but isn't used currently ;-)) */
case SYS_ix_startup: /* those two call longjmp thru _exit */
case SYS_ix_exec_entry:
case SYS_fork:
case SYS_floor: /* have all functions returning more than */
case SYS_ceil: /* 4 bytes be called JMP'y */
case SYS_atof:
case SYS_frexp:
case SYS_modf:
case SYS_ldexp:
case SYS_atan ... SYS_fabs: /* all the trigo stuff from *transbase.. */
case SYS_strtod:
te_action = TRACE_ACTION_JMP;
break;
default:
te_action = TRACE_ACTION_JSR;
break;
}
/* don't use syscall() here.. */
omask = sigsetmask (~0);
if (! sem_initialized)
{
sem_initialized = 1;
InitSemaphore (& psem);
}
ObtainSemaphore (& psem);
handler_active = 0;
for (tp = (struct trace_packet *) packets.lh_Head;
ntp = (struct trace_packet *) tp->tp_message.mn_Node.ln_Succ;
tp = ntp)
{
if ((!tp->tp_pid || tp->tp_pid == (pid_t) me) &&
(!tp->tp_syscall || tp->tp_syscall == scall))
{
Remove ((struct Node *) tp);
tp->tp_is_entry = 1;
tp->tp_argv = &scall;
tp->tp_errno = u.u_errno;
/* provide the default for the handler to (possibly) override */
tp->tp_action = te_action;
/* wanted to use u.u_sync_mp here, but this leads to some
deadlock situations when the port is used for packets.. */
tp->tp_message.mn_ReplyPort = (struct MsgPort *) me;
SetSignal (0, SIGBREAKF_CTRL_E);
PutMsg (tp->tp_tracer_port, (struct Message *) tp);
Wait (SIGBREAKF_CTRL_E);
/* the last handler wins ;-)) */
te_action = tp->tp_action;
handler_active = 1;
/* should be safe.. */
AddHead (&packets, (struct Node *) tp);
}
}
ReleaseSemaphore (& psem);
sigsetmask (omask);
if (! handler_active)
te_action = TRACE_ACTION_JMP;
switch (te_action)
{
case TRACE_ACTION_ABORT:
abort();
default:
case TRACE_ACTION_JMP:
return TRACE_ACTION_JMP;
case TRACE_ACTION_JSR:
{
int result, error;
/* we now know that there is at least one trace handler
interested in this result, so do the extra overhead of
calling with (excess) argument copying */
result = func (a1, a2, a3, a4, a5, a6, a7, a8, a9, aa, ab, ac, ad, ae, af);
error = errno;
/* replace the function address with the result */
*(int *)&func = result;
/* and repeat the process of calling the trace handler(s) */
omask = sigsetmask (~0);
ObtainSemaphore (& psem);
for (tp = (struct trace_packet *) packets.lh_Head;
ntp = (struct trace_packet *) tp->tp_message.mn_Node.ln_Succ;
tp = ntp)
{
if ((!tp->tp_pid || tp->tp_pid == (pid_t) me) &&
(!tp->tp_syscall || tp->tp_syscall == scall))
{
Remove ((struct Node *) tp);
tp->tp_is_entry = 0;
tp->tp_argv = &scall;
errno = error;
KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
tp->tp_errno = u.u_errno;
tp->tp_message.mn_ReplyPort = (struct MsgPort *) me;
SetSignal (0, SIGBREAKF_CTRL_E);
PutMsg (tp->tp_tracer_port, (struct Message *) tp);
Wait (SIGBREAKF_CTRL_E);
error = errno;
/* should be safe.. */
AddHead (&packets, (struct Node *) tp);
}
}
ReleaseSemaphore (& psem);
sigsetmask (omask);
errno = error;
KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
}
/* fall into */
case TRACE_ACTION_RTS:
return TRACE_ACTION_RTS;
}
}
#endif /* TRACE_LIBRARY */
int
tracecntl (enum trace_cmd cmd, struct trace_packet *tp)
{
#ifndef TRACE_LIBRARY
errno = ENOSYS;
KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
return -1;
#else
switch (cmd)
{
case TRACE_INSTALL_HANDLER:
ix_lock_base ();
tp->tp_message.mn_Node.ln_Type = NT_MESSAGE;
tp->tp_message.mn_Length = sizeof (struct trace_packet);
ObtainSemaphore (& psem);
AddTail (&packets, (struct Node *) tp);
ReleaseSemaphore (& psem);
ix_unlock_base ();
u.u_trace_flags = 1;
errno = 0;
KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
return errno;
case TRACE_REMOVE_HANDLER:
ix_lock_base ();
ObtainSemaphore (& psem);
Remove ((struct Node *) tp);
ReleaseSemaphore (& psem);
ix_unlock_base ();
u.u_trace_flags = 0;
errno = 0;
KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
return errno;
default:
errno = EINVAL;
KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
return -1;
}
#endif
}